package com.zoro.barn.business.service.sqldocument.business.document.service;

import com.zoro.barn.business.service.sqldocument.business.document.dao.BarnDocumentImageMapper;
import com.zoro.barn.business.service.sqldocument.business.document.dao.BarnDocumentInfoMapper;
import com.zoro.barn.business.service.sqldocument.business.document.dao.BarnDocumentUseMapper;
import com.zoro.barn.business.service.sqldocument.business.document.entity.BarnDocumentImage;
import com.zoro.barn.business.service.sqldocument.business.document.entity.BarnDocumentInfo;
import com.zoro.barn.business.service.sqldocument.business.document.entity.BarnDocumentUse;
import com.zoro.barn.business.service.sqldocument.conf.MongoDbConfig;
import com.zoro.barn.commons.foundation.exceptions.DataIncompleteException;
import com.zoro.barn.commons.mongodb.dao.MongoDao;
import com.zoro.barn.commons.tools.encryption.EncryptUtils;
import com.zoro.barn.commons.tools.string.IDs;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author zhaoxingwu
 */
@Service
public class BarnDocumentInfoService {

    private final static String DOCUMENT_ID_KEY = "documentId";

    @Resource
    private BarnDocumentInfoMapper barnDocumentInfoMapper;
    @Resource
    private BarnDocumentUseMapper barnDocumentUseMapper;
    @Resource
    private BarnDocumentImageMapper barnDocumentImageMapper;
    private final MongoDao mongoDao;
    private final MongoDbConfig mongoDbConfig;

    public BarnDocumentInfoService(MongoDao mongoDao, MongoDbConfig mongoDbConfig) {
        this.mongoDao = mongoDao;
        this.mongoDbConfig = mongoDbConfig;
    }


    @Transactional(rollbackFor = Exception.class)
    public BarnDocumentInfo insert(BarnDocumentInfo info, String text, String plainText, String[] projectIds, String[] imgIds) {

        if (StringUtils.isBlank(info.getCreateId())) {
            throw new DataIncompleteException("create id can not null");
        }

        if (StringUtils.isBlank(info.getDocumentTitle())) {
            throw new DataIncompleteException("Document title can not  empty");
        }


        if (StringUtils.isBlank(info.getProductId())) {
            throw new DataIncompleteException("Product can not  empty");
        }

        info.setPid(IDs.uuid());
        info.setCreateTime(new Date());
        // save document text to mongo
        String fileId = null;
        if (StringUtils.isNotBlank(text)) {
            fileId = this.uploadLoad(text, info.getPid(), info.getDocumentTitle());
        }
        info.setFileId(fileId);
        String plainFileId = null;
        if (StringUtils.isNotBlank(plainText)) {
            plainFileId = this.uploadLoad(plainText, info.getPid(), info.getDocumentTitle() + "_plain_text");
        }
        info.setPlainTextId(plainFileId);

        if (!StringUtils.equals(BarnDocumentInfo.RELEASE_YES, info.getIsRelease())) {
            info.setIsRelease(BarnDocumentInfo.RELEASE_NO);
        } else {
            info.setReleaseTime(new Date());
        }
        // save basic info
        barnDocumentInfoMapper.insert(info);
        // save related projects
        if (projectIds != null && projectIds.length > 0) {
            List<BarnDocumentUse> useList = new ArrayList<>();
            for (String id : projectIds) {
                BarnDocumentUse use = new BarnDocumentUse();
                use.setPid(IDs.uuid());
                use.setProjectId(id);
                use.setDocumentId(info.getPid());
                useList.add(use);
            }
            barnDocumentUseMapper.insertBatch(useList);
        }
        // save image ids;
        if (imgIds != null && imgIds.length > 0) {
            List<BarnDocumentImage> images = new ArrayList<>();
            for (String id : imgIds) {
                BarnDocumentImage image = new BarnDocumentImage();
                image.setPid(IDs.uuid());
                image.setDocumentId(info.getPid());
                image.setFileId(id);
                images.add(image);
            }
            barnDocumentImageMapper.insertBatch(images);
        }
        return info;
    }

    @Transactional(rollbackFor = Exception.class)
    public BarnDocumentInfo update(BarnDocumentInfo info, String text, String plainText, String[] projectIds, String[] imgIds) {

        if (StringUtils.isBlank(info.getPid())) {
            throw new DataIncompleteException(" pid can not null");
        }
        if (StringUtils.isBlank(info.getDocumentTitle())) {
            throw new DataIncompleteException("Document title can not  empty");
        }
        if (StringUtils.isBlank(info.getProductId())) {
            throw new DataIncompleteException("Product can not  empty");
        }

        // 先查原来的
        BarnDocumentInfo original = this.barnDocumentInfoMapper.selectById(info.getPid());
        if (!StringUtils.equals(info.getIsRelease(), BarnDocumentInfo.RELEASE_YES)) {
            // 不发布
            info.setIsRelease(BarnDocumentInfo.RELEASE_NO);
            info.setReleaseTime(null);
        } else {
            // 发布
            info.setIsRelease(BarnDocumentInfo.RELEASE_YES);
            if (!StringUtils.equals(original.getIsRelease(), BarnDocumentInfo.RELEASE_YES)) {
                info.setReleaseTime(new Date());
            } else {
                info.setReleaseTime(original.getReleaseTime());
            }
        }
        // 上传mongo
        String fileId = null;
        if (StringUtils.isNotBlank(text)) {
            fileId = this.uploadLoad(text, info.getPid(), info.getDocumentTitle());
        }
        info.setFileId(fileId);

        String plainFileId = null;
        if (StringUtils.isNotBlank(plainText)) {
            plainFileId = this.uploadLoad(plainText, info.getPid(), info.getDocumentTitle() + "_plain_text");
        }
        info.setPlainTextId(plainFileId);

        // 删除原来的 use
        this.barnDocumentUseMapper.deleteByDocumentId(info.getPid());
        // 插入新的 use
        if (projectIds != null && projectIds.length > 0) {
            List<BarnDocumentUse> useList = Arrays.stream(projectIds).map(v -> {
                BarnDocumentUse use = new BarnDocumentUse();
                use.setDocumentId(info.getPid());
                use.setProjectId(v);
                use.setPid(IDs.uuid());
                return use;
            }).collect(Collectors.toList());
            this.barnDocumentUseMapper.insertBatch(useList);
        }
        // 查出原来的 image
        List<BarnDocumentImage> images = this.barnDocumentImageMapper.getByDocumentId(info.getPid());
        // 删除document中删除的 image
        Set<String> oldImageId = new HashSet<>();
        if (CollectionUtils.isNotEmpty(images)) {
            images.forEach(v -> oldImageId.add(v.getFileId()));
        }
        Set<String> newImageId = new HashSet<>(Arrays.asList(imgIds));
        if (CollectionUtils.isNotEmpty(oldImageId)) {
            oldImageId.removeAll(newImageId);
            if (CollectionUtils.isNotEmpty(oldImageId)) {
                oldImageId.forEach(v -> this.mongoDao.deleteById(this.mongoDbConfig.getDocumentImageDb(), v));
            }
        }
        // 插入新的 image
        this.barnDocumentImageMapper.deleteByDocumentId(info.getPid());
        List<BarnDocumentImage> imageList = Arrays.stream(imgIds).map(v -> {
            BarnDocumentImage i = new BarnDocumentImage();
            i.setPid(IDs.uuid());
            i.setDocumentId(info.getPid());
            i.setFileId(v);
            return i;
        }).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(imageList)) {
            this.barnDocumentImageMapper.insertBatch(imageList);
        }

        // update info
        // 删除原来的 mongo 中的 file
        if (StringUtils.isNotBlank(original.getFileId())) {
            this.mongoDao.deleteById(this.mongoDbConfig.getDocumentTextDb(), original.getFileId());
        }
        if (StringUtils.isNotBlank(original.getPlainTextId())) {
            this.mongoDao.deleteById(this.mongoDbConfig.getDocumentTextDb(), original.getPlainTextId());
        }
        this.barnDocumentInfoMapper.updateByPid(info);
        return info;
    }

    /**
     * 条件查询
     *
     * @param info 查询条件
     * @return 查询结果
     */
    public List<BarnDocumentInfo> query(BarnDocumentInfo info) {
        return this.barnDocumentInfoMapper.query(info);
    }

    /**
     * 主键查询document 基本数据
     *
     * @param pid 主键值
     * @return 基本信息
     */
    public BarnDocumentInfo getById(String pid) {
        return this.barnDocumentInfoMapper.selectById(pid);
    }

    /**
     * 查询 document 正文
     *
     * @param textId id
     * @return 正文
     */
    public String getDocumentText(String textId) {
        if (StringUtils.isBlank(textId)) {
            return null;
        }
        ByteArrayOutputStream os = this.mongoDao.download(this.mongoDbConfig.getDocumentTextDb(), textId);
        return os.toString();
    }

    /**
     * 查询图片id
     *
     * @param documentId documentId
     * @return 图片id
     */
    public List<BarnDocumentImage> getDocumentImages(String documentId) {
        return this.barnDocumentImageMapper.getByDocumentId(documentId);
    }

    /**
     * 查询 project 使用情况
     *
     * @param documentId document
     * @return result
     */
    public List<BarnDocumentUse> getDocumentUse(String documentId) {
        return this.barnDocumentUseMapper.getByDocumentId(documentId);
    }

    @Transactional
    public void deleteDocument(String documentId) {
        if (StringUtils.isBlank(documentId)) {
            return;
        }
        // 删除文档
        this.barnDocumentInfoMapper.deleteById(documentId);
        // 删除use
        this.barnDocumentUseMapper.deleteByDocumentId(documentId);
        // 删除 use
        List<BarnDocumentImage> imageList = this.barnDocumentImageMapper.getByDocumentId(documentId);
        if (CollectionUtils.isNotEmpty(imageList)) {
            this.barnDocumentImageMapper.deleteByDocumentId(documentId);
            imageList.forEach(v -> this.mongoDao.deleteById(this.mongoDbConfig.getDocumentImageDb(), v.getFileId()));
        }
    }

    private String uploadLoad(String text, String documentId, String documentTitle) {
        ByteArrayInputStream is = new ByteArrayInputStream(text.getBytes(StandardCharsets.UTF_8));
        Map<String, Object> meta = new HashMap<>();
        meta.put(DOCUMENT_ID_KEY, documentId);
        String sha = EncryptUtils.sha256(text);
        return this.mongoDao.upload(this.mongoDbConfig.getDocumentTextDb(), is, documentTitle + ".html", meta, sha);
    }
}
